import { LayoutSpecification } from 'mapbox-gl';
import { EngiGis } from '..';
import { BaseLayer, IBaseLayer } from '../map/layers/BaseLayer';
import { SharedTextLayer } from './shared/sharedTextLayer';
import { IBizUnitConfig } from '../shared';
import { EventBus } from '../utils/eventBus';

/**
 * 业务单元基类，所有业务单元都需要继承此类
 * @param engGis EngiGis 实例
 * @param layers 业务单元对应的图层， 可以是多个图层. 第一个图层会被用作共享文本图层
 */
export abstract class BizUnit {
  engiGis!: EngiGis; // 引擎实例
  layers: BaseLayer[] = [];

  abstract unitType: string; // 业务单元类型：对于模型就是 modelType, 对于其他的，，就自己写
  protected sizeScale = 1;
  private bus: EventBus = new EventBus(); // 当前业务单元的事件总线， 用于处理业务单元内部的事件
  sharedTextLayer!: SharedTextLayer;
  sort: number = 1;
  // 数据源ID， 每个业务单元都有一个唯一的数据源ID
  get sourceId(): string {
    return `${this.unitType}_source`;
  }
  // 图层ID， 每个业务单元都有一个唯一的图层ID
  get layerId(): string {
    return `${this.unitType}_layer`;
  }

  get map() {
    return this.engiGis.mapCore.map;
  }

  /**
   * 业务单元基类构造函数
   * @param engiGis EngiGis 实例
   * @param layers 业务单元对应的图层， 可以是多个图层. 第一个图层会被用作共享文本图层
   */
  constructor(engiGis: EngiGis, ...layers: BaseLayer[]) {
    this.engiGis = engiGis;
    this.layers = layers;
    // 初始化共享文本图层
    this.sharedTextLayer = engiGis.getSharedTextLayer();
  }

  /**
   * 获取图层的基础布局配置
   */
  protected getLayoutBase(): LayoutSpecification {
    return generateCommonLayout({ sizeScale: this.sizeScale });
  }

  // addLayersToMap(layers: IBaseLayer[]): void {
  // layers.forEach(layer => {
  //   layer.render(map);
  // })
  // }

  // initInteractions() {
  //   const engiGis = this.engiGis;
  //   const eventName = `click:${this.layerId}`;
  //   engiGis.addInteraction(eventName, {
  //     type: 'click',
  //     layers: [this.layerId],
  //     handler(e) {
  //       engiGis.bus.emit(eventName, e);
  //       return true; // 阻止传播
  //     }
  //   })
  // }

  /**
   * 触发指定事件，并将参数传递给所有注册的监听器
   * @param event 事件名称
   * @param args 传递给事件监听器的参数
   */
  emit(event: string, ...args: any[]): void {
    this.bus.emit(event, ...args);
  }

  /**
   * 注册事件监听器
   * @param event 要监听的事件名称
   * @param callback 事件触发时执行的回调函数
   * @returns 当前实例，支持链式调用
   */
  on(event: string, callback: (...args: any[]) => void) {
    this.bus.on(event, callback);
  }

  /**
   * 移除事件监听器
   * @param event 事件名称
   * @param callback 可选，要移除的特定回调函数。如果不提供，将移除该事件的所有监听器
   */
  off(event: string, callback?: (...args: any[]) => void) {
    this.bus.off(event, callback);
  }

  /**
   * 注册一次性事件监听器，触发后自动移除
   * @param event 要监听的事件名称
   * @param callback 事件触发时执行的回调函数
   */
  once(event: string, callback: (...args: any[]) => void) {
    this.bus.once(event, callback);
  }

  /**
   * 设置渲染逻辑
   * @param config 包含图层渲染顺序和其他配置
   * @returns 返回一个图层数组， 可能是多个图层, engiGis 会自动管理这些图层
   */
  abstract setRenderer(config: IBizUnitConfig): Promise<IBaseLayer[]>;
  /**
   * 更新地理数据
   * 把地理信息数据更新到 mapbox 实例的 source 中
   * 例如： 内置模型对象的操作 是把模型地理数据(from indexeddb/api) 更新进去
   * 注意： 默认是 engiGis 会在 addBizUnit 时自动调用。 如果后续有更新地理数据， 可手动调用
   */
  abstract updateGeoData(config: IBizUnitConfig | null): Promise<void>;
  /**
   * 更新业务单元数据， 自动触发重新渲染
   * @param data 更新数据
   */
  async update(
    update: { id: string; properties: Record<string, any> }[],
  ): Promise<void> {
    update.forEach(item => {
      item.id = `${this.unitType}.${item.id}`;
    })
    this.engiGis.mapCore.updateSourceWithProperties(this.sourceId, update);
  }
}

export const sizeExpr = (scale: number) => {
  const temp = [];
  for (let i = 1; i <= 24; i++) {
    temp.push(i, ['*', scale, ['number', ['get', `z${i}`], 0]]);
  }
  const expr = ['interpolate', ['linear'], ['zoom'], ...temp];
  return expr as LayoutSpecification['icon-size'];
};

export const generateCommonLayout = (
  config: { sizeScale: number } = { sizeScale: 1 },
): LayoutSpecification => {
  return {
    'icon-size': sizeExpr(config.sizeScale),
    'icon-padding': 0,
    'text-font': ['Open Sans Regular'],
    'text-anchor': 'top-left',
    'text-offset': [
      'interpolate',
      ['linear'],
      ['zoom'],
      0,
      [0, 0],
      10,
      [0, 0],
      22,
      [0.2, 0.2],
    ],
    visibility: 'none',
  };
};
